package org.viettan.android.main.managers;

import java.io.IOException;
import java.util.List;

import org.viettan.android.main.io.EntityRetrievalException;
import org.viettan.android.main.io.NetworkException;
import org.viettan.android.main.io.NetworkUtility;
import org.viettan.android.main.model.Entity;
import org.viettan.android.main.xml.BaseXmlParser;
import org.xmlpull.v1.XmlPullParserException;

import android.net.Uri;

import com.google.common.base.Preconditions;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.internal.Nullable;

/**
 * Singleton to manage retrieving the {@link Entity}s from the Viet Tan website feed.
 */
@Singleton
public class BaseEntityManager<T> {

    private final NetworkUtility networkUtility;
    private final BaseXmlParser<T> xmlParser;

    @Inject
    BaseEntityManager(NetworkUtility networkUtility, BaseXmlParser<T> xmlParser) {
	this.networkUtility = networkUtility;
	this.xmlParser = xmlParser;
    }

    /**
     * Returns a full list of {@link Entity}s.
     *
     * @param xmlUrl
     *            URL to the XML source
     * @param useCache
     *            retrieve the data from the cache if it is there
     * @return List of {@link Entity}s
     * @throws EntityRetrievalException
     */
    protected List<T> getEntities(Uri xmlUrl, boolean useCache) throws EntityRetrievalException {
	return getEntities(xmlUrl, useCache, null);
    }

    /**
     * Returns a full list of {@link Entity}s.
     *
     * @param xmlUrl
     *            URL to the XML source
     * @param useCache
     *            retrieve the data from the cache if it is there
     * @param parentNodeTagName
     *            if provided will only parse the entities that have this tag name as an ancestor
     * @return List of {@link Entity}s
     * @throws EntityRetrievalException
     */
    protected List<T> getEntities(Uri xmlUrl, boolean useCache, @Nullable String parentNodeTagName)
	    throws EntityRetrievalException {
	Preconditions.checkArgument(xmlUrl != null, "xmlUrl cannot be null");

	try {
	    String xml = networkUtility.httpGet(xmlUrl, useCache);
	    return getEntities(xml, parentNodeTagName);
	} catch (NetworkException e) {
	    throw new EntityRetrievalException(e);
	}
    }

    /**
     * Parses an XML and returns a full list of {@link Entity}s.
     *
     * @param xml
     *            full XML source to be parsed
     * @param parentNodeTagName
     *            if provided will only parse the entities that have this tag name as an ancestor
     * @return List of {@link Entity}s
     * @throws EntityRetrievalException
     */
    protected List<T> getEntities(String xml, @Nullable String parentNodeTagName)
	    throws EntityRetrievalException {
	Preconditions.checkArgument(xml != null, "xml cannot be null");

	try {
	    return xmlParser.parseMultiple(xml, parentNodeTagName);
	} catch (XmlPullParserException e) {
	    throw new EntityRetrievalException(e);
	} catch (IOException e) {
	    throw new EntityRetrievalException(e);
	}
    }

    /**
     * Returns the full {@code Entity} downloaded from the given url to an XML source.
     *
     * @param xmlUrl
     *            URL to the XML representation of the Entity
     * @return fully-populated {@code Entity}
     * @throws EntityRetrievalException
     */
    protected T getEntity(Uri xmlUrl, boolean useCache) throws EntityRetrievalException {
	Preconditions.checkArgument(xmlUrl != null, "xmlUrl cannot be null");

	try {
	    String xml = networkUtility.httpGet(xmlUrl, useCache);
	    return xmlParser.parseSingle(xml);
	} catch (NetworkException e) {
	    throw new EntityRetrievalException(e);
	} catch (XmlPullParserException e) {
	    throw new EntityRetrievalException(e);
	} catch (IOException e) {
	    throw new EntityRetrievalException(e);
	}
    }
}
